﻿/**
 * @file std_mdebug.c
 * Copyright (c) 2019 Gaaagaa. All rights reserved.
 * 
 * @author  : Gaaagaa
 * @date    : 2019-09-09
 * @version : 1.0.0.0
 * @brief   : 用于调试操作时，记录内存分配情况的相关工作流程。
 */

/**
 * The MIT License (MIT)
 * Copyright (c) Gaaagaa. All rights reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "std_mdebug.h"
#include "std_debug.h"
#include "std_rbtree.h"

#include <stdlib.h>
#include <string.h>

////////////////////////////////////////////////////////////////////////////////

#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif // __GNUC__

////////////////////////////////////////////////////////////////////////////////

/**********************************************************/
/**
 * @brief 执行内存块分配的回调函数。
 */
static xstd_mptr_t debug_allocate(xstd_size_t xst_size)
{
    return malloc(xst_size);
}

/**********************************************************/
/**
 * @brief 执行内存块释放的回调函数。
 */
static xstd_void_t debug_dealloc(xstd_mptr_t xmt_mptr)
{
    if (XSTD_NULL != xmt_mptr)
        free(xmt_mptr);
}

/**********************************************************/
/**
 * @brief 节点存储的元素对象 的 拷贝构造操作 的回调函数。
 * 
 * @param [out] xet_dptr : 目标存储的元素对象。
 * @param [in ] xet_sptr : 源数据的元素对象。
 * @param [in ] xet_ctxt : 节点元素的上下文信息。
 */
static xstd_void_t debug_copyfrom(
                        xstd_elem_t xet_dptr,
                        xstd_elem_t xet_sptr,
                        xstd_ctxt_t xet_ctxt)
{
    memcpy(xet_dptr, xet_sptr, xet_ctxt->xstd_esize);
}

/**********************************************************/
/**
 * @brief 节点存储的元素对象 的 右值 move 拷贝构造操作 的回调函数。
 * 
 * @param [out] xet_dptr : 目标存储的元素对象。
 * @param [in ] xet_sptr : 源数据的元素对象。
 * @param [in ] xet_ctxt : 节点元素的上下文信息。
 */
static xstd_void_t debug_copymove(
                        xstd_elem_t xet_dptr,
                        xstd_elem_t xet_sptr,
                        xstd_ctxt_t xet_ctxt)
{
    memcpy(xet_dptr, xet_sptr, xet_ctxt->xstd_esize);
}

/**********************************************************/
/**
 * @brief 节点存储的元素对象 的 析构操作 的回调函数。
 * 
 * @param [out] xet_dptr : 节点存储的元素对象。
 * @param [in ] xet_ctxt : 节点元素的上下文信息。
 */
static xstd_void_t debug_destruct(
                        xstd_elem_t xet_dptr,
                        xstd_ctxt_t xet_ctxt)
{
    memset(xet_dptr, 0, xet_ctxt->xstd_esize);
}

/** 调试操作相关的 通用内存管理对象 */
static const xstd_allocator_t xstd_altor_debug =
{
    &debug_allocate,
    &debug_dealloc ,
    &debug_copyfrom,
    &debug_copymove,
    &debug_destruct
};

////////////////////////////////////////////////////////////////////////////////

//====================================================================

// 
// xstd_mdebug_t 的相关数据类型、宏
// 

/**
 * @struct xmdb_node_t
 * @brief  记录每个内存分配操作的相关信息。
 */
typedef struct xmdb_node_t
{
    xmdb_mptr_t    xmdb_mptr;   ///< 分配的内存块
    xmdb_size_t    xmdb_size;   ///< 内存块大小
} xmdb_node_t, * xmdb_nptr_t;

/** 取 xmdb_node_t 节点键值的操作宏 */
#define XMDB_KEY(xmdb_eptr) ((xmdb_size_t)((xmdb_nptr_t)(xmdb_eptr))->xmdb_mptr)

/**
 * @struct xstd_mdebug_t
 * @brief  统计内存分配的 工作对象 的结构体描述信息。
 */
typedef struct xstd_mdebug_t
{
    xrbt_this_t    xmdb_table;  ///< 存储内存分配信息的映射表
    xmdb_size_t    xmdb_count;  ///< 记录当前统计分配的内存大小
} xstd_mdebug_t;

//====================================================================

// 
// xstd_mdebug_t 的内部相关操作接口
// 

/**********************************************************/
/**
 * @brief 对两个 xstd_mdebug_t 节点元素对象 进行 “小于” 比较操作的回调函数。
 * 
 * @param [in ] xet_lptr : 比较操作的左值元素对象。
 * @param [in ] xet_rptr : 比较操作的右值元素对象。
 * @param [in ] xet_ctxt : 节点元素的上下文信息。
 * 
 * @return xstd_bool_t : 比较结果。
 */
static xstd_bool_t mdebug_node_lt(
                        xstd_elem_t xet_lptr,
                        xstd_elem_t xet_rptr,
                        xstd_ctxt_t xet_ctxt)
{
    return (XMDB_KEY(xet_lptr) < XMDB_KEY(xet_rptr));
}

//====================================================================

// 
// xstd_mdebug_t 的外部相关操作接口
// 

/**********************************************************/
/**
 * @brief 创建 xstd_mdebug_t 对象。
 */
xmdb_this_t std_mdebug_create(void)
{
    //======================================

    static const xstd_context_t xmdb_ctxt =
    {
        0xFFFFFFFF,
        sizeof(xmdb_node_t),
        XMDB_NULL,
        (xstd_altor_t)&xstd_altor_debug
    };

    //======================================

    xmdb_this_t xmdb_this = 
        xstd_altor_debug.xfunc_allocate(sizeof(xstd_mdebug_t));
    XASSERT(XSTD_NULL != xmdb_this);

    xmdb_this->xmdb_table = std_rbtree_create(&xmdb_ctxt, &mdebug_node_lt);
    XASSERT(XSTD_NULL != xmdb_this->xmdb_table);

    xmdb_this->xmdb_count = 0;

    //======================================

    return xmdb_this;
}

/**********************************************************/
/**
 * @brief 销毁 xstd_mdebug_t 对象。
 */
xmdb_void_t std_mdebug_destroy(xmdb_this_t xmdb_this)
{
    XASSERT(XSTD_NULL != xmdb_this);

    std_rbtree_destroy(xmdb_this->xmdb_table);
    xstd_altor_debug.xfunc_dealloc(xmdb_this);
}

/**********************************************************/
/**
 * @brief 当前 xstd_mdebug_t 对象所统计分配到的内存大小。
 */
xmdb_size_t std_mdebug_count(xmdb_this_t xmdb_this)
{
    XASSERT(XSTD_NULL != xmdb_this);
    return xmdb_this->xmdb_count;
}

/**********************************************************/
/**
 * @brief 当前 xstd_mdebug_t 对象所统计分配内存块的数量。
 */
xmdb_size_t std_mdebug_size(xmdb_this_t xmdb_this)
{
    XASSERT(XSTD_NULL != xmdb_this);
    return std_rbtree_size(xmdb_this->xmdb_table);
}

/**********************************************************/
/**
 * @brief 从 xstd_mdebug_t 对象中获取所记录内存块的大小。
 * 
 * @param [in,out] xmdb_this : xstd_mdebug_t 对象。
 * @param [in    ] xmdb_mptr : 所记录的内存块。
 * 
 * @return xmdb_size_t : 
 * 所记录内存块的大小。为 0 时，表示其未找到记录的信息。
 */
xmdb_size_t std_mdebug_msize(
                xmdb_this_t xmdb_this,
                xmdb_mptr_t xmdb_mptr)
{
    XASSERT(XSTD_NULL != xmdb_this);
    XASSERT(XSTD_NULL != xmdb_mptr);

    xmdb_node_t xmdb_node = { xmdb_mptr, 0 };
    xrbt_iter_t xrbt_iter = std_rbtree_find(xmdb_this->xmdb_table, &xmdb_node);
    if (std_rbtree_isnil(xrbt_iter))
    {
        return 0;
    }

    return ((xmdb_nptr_t)std_rbtree_elem(xrbt_iter))->xmdb_size;
}

/**********************************************************/
/**
 * @brief 将新分配的内存信息，记录到 xstd_mdebug_t 对象中。
 * 
 * @param [in,out] xmdb_this : xstd_mdebug_t 对象。
 * @param [in    ] xmdb_mptr : 新分配的内存块。
 * @param [in    ] xmdb_size : 内存块大小。
 * 
 * @return xmdb_bool_t : 操作是否成功。
 */
xmdb_bool_t std_mdebug_record(
                xmdb_this_t xmdb_this,
                xmdb_mptr_t xmdb_mptr,
                xmdb_size_t xmdb_size)
{
    XASSERT(XSTD_NULL != xmdb_this);
    XASSERT(XSTD_NULL != xmdb_mptr);
    XASSERT(xmdb_size > 0);

    xmdb_bool_t xmdb_clash = XMDB_FALSE;
    xmdb_node_t xmdb_vnode = { xmdb_mptr, xmdb_size };

    std_rbtree_insert(xmdb_this->xmdb_table, &xmdb_vnode, &xmdb_clash);
    if (xmdb_clash)
    {
        return XMDB_FALSE;
    }

    xmdb_this->xmdb_count += xmdb_size;

    return XMDB_TRUE;
}

/**********************************************************/
/**
 * @brief 将待释放的内存块，从 xstd_mdebug_t 对象中删除对应的记录信息。
 * 
 * @param [in,out] xmdb_this : xstd_mdebug_t 对象。
 * @param [in    ] xmdb_mptr : 待释放的内存块。
 * 
 * @return xmdb_bool_t : 操作是否成功。
 */
xmdb_bool_t std_mdebug_remove(
                xmdb_this_t xmdb_this,
                xmdb_mptr_t xmdb_mptr)
{
    XASSERT(XSTD_NULL != xmdb_this);
    XASSERT(XSTD_NULL != xmdb_mptr);

    xmdb_nptr_t xmdb_nptr = XMDB_NULL;
    xmdb_node_t xmdb_node = { xmdb_mptr, 0 };
    xrbt_iter_t xrbt_iter = std_rbtree_find(xmdb_this->xmdb_table, &xmdb_node);

    if (std_rbtree_isnil(xrbt_iter))
    {
        return XMDB_FALSE;
    }

    xmdb_nptr = (xmdb_nptr_t)std_rbtree_elem(xrbt_iter);
    XASSERT(xmdb_this->xmdb_count >= xmdb_nptr->xmdb_size);
    xmdb_this->xmdb_count -= xmdb_nptr->xmdb_size;

    std_rbtree_erase(xmdb_this->xmdb_table, xrbt_iter);

    return XMDB_TRUE;
}

////////////////////////////////////////////////////////////////////////////////

#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif // __GNUC__

////////////////////////////////////////////////////////////////////////////////

